package com.mytest.provider.rl.test;

import redis.clients.jedis.Jedis;

import java.time.LocalTime;

import static com.mytest.provider.rl.test.LockConstants.NOT_EXIST;
import static com.mytest.provider.rl.test.LockConstants.OK;
import static com.mytest.provider.rl.test.LockConstants.SECONDS;

/**
 * @Author: llq
 * @Date: 2020/5/18
 */
public class LockCase extends RedisLock {
    public LockCase(Jedis jedis, String name) {
        super(jedis, name);
    }

    /**
     * 等同于
     * SET lockKey value NX
     */
    @Override
    public void lock() {
        while(true){
            //NOT_EXIST只有redis里没有key为lockkey,才能set入redis中,就是保证在使用的锁,就不可以被其他服务使用了
            //1 不加过期时间，会导致拿到锁的服务宕机了，别的服务就永远拿不到锁
            //2 "value" value固定值 可能会导致锁误删,这里使用个随机值
            String result = jedis.set(lockKey,lockValue,NOT_EXIST,SECONDS,30);
            if(OK.equals(result)){
                System.out.println("线程id:"+Thread.currentThread().getId() + "加锁成功!时间:"+ LocalTime.now());

                //开启定时刷新过期时间,就是在调用锁过程中,锁永不过期
                //如果服务宕机了,就不会继续刷新锁的过期时间,自动30秒后锁过期
                isOpenExpirationRenewal = true;
                scheduleExpirationRenewal();
                break;
            }
            System.out.println("线程id:"+Thread.currentThread().getId() + "获取锁失败，休眠10秒!时间:"+LocalTime.now());
            //休眠10秒
            /*
             * 延迟100 毫秒, 这里使用随机时间可能会好一点,可以防止饥饿进程的出现,即,当同时到达多个进程,
             * 只会有一个进程获得锁,其他的都用同样的频率进行尝试,后面有来了一些进行,也以同样的频率申请锁,这将可能导致前面来的锁得不到满足.
             * 使用随机的等待时间可以一定程度上保证公平性
             * Thread.sleep（10）让线程睡眠10秒，让系统重新计算所有的进程的优先级，
             * 降低自己的优先级，隐式的提高了其他进程的优先级，有效防止饥饿进程的出现
             * 系统进程优先级（优先级+饥饿时间统一计算的）
             */
            sleepBySencond(10);
        }
    }

    @Override
    public void unlock() {
        /*//那自己的value与现在锁的value做比较
        String lockvalue = jedis.get(lockKey);
        if (lockValue.equals(lockvalue)){
            jedis.del(lockKey);
        }*/

        // 使用lua脚本进行原子删除操作,lockkey-->KEYS[1] ,lockvalue-->ARGV[1]

        String checkAndDelScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                "return redis.call('del', KEYS[1]) " +
                "else " +
                "return 0 " +
                "end";
        //使用eval对lua脚本求值
        jedis.eval(checkAndDelScript, 1, lockKey, lockValue);
        //停止刷新过期时间的线程轮询
        isOpenExpirationRenewal = false;
    }
}
